package com.itany.mall.jdbc.template;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import com.itany.mall.jdbc.exception.DataAccessException;
import com.itany.mall.jdbc.template.callback.ConnectionCallback;
import com.itany.mall.jdbc.template.callback.PreparedStatementCreater;
import com.itany.mall.jdbc.template.callback.ResultSetExtractor;
import com.itany.mall.jdbc.template.callback.RowMapper;
import com.itany.mall.jdbc.util.JDBCUtil;

/**
 * JDBC模板
 * @author 赵云
 */
public class JDBCTemplate<K, T> {

	/**
	 * 执行insert/update/delete
	 * @param sql 要执行的预编译语句
	 * @param params 与预编译语句中占位符对应的参数
	 */
	public void update(String sql, Object[] params) {
		Connection con = null;
		PreparedStatement ps = null;
		try {
			con = JDBCUtil.getConnection();
			ps = con.prepareStatement(sql);
			if (params != null && params.length > 0) {
				for (int i = 0; i < params.length; i++) {
					ps.setObject(i + 1, params[i]);
				}
			}
			ps.executeUpdate();
		} catch (Exception e) {
			throw new DataAccessException("数据库访问异常", e);
		} finally {
			// Connection要在事务管理中关闭
			// dao层不能关Connection
			JDBCUtil.close(null, ps, null);
		}
	}

	/**
	 * 保存并返回主键
	 * @param creater 创建预编译状态集的回调接口, 由该模板方法的调用者将状态集创建出来, 交给模板方法使用
	 * @param holder 用于携带生成的主键的参数对象, 通过该对象将模板方法中返回的主键值携带出去
	 */
	public void update(PreparedStatementCreater creater, KeyHolder<K> holder) {
		Connection con = null;
		PreparedStatement ps = null;
		ResultSet res = null;
		try {
			con = JDBCUtil.getConnection();
			ps = creater.createPreparedStatement(con);
			ps.executeUpdate();
			res = ps.getGeneratedKeys();
			List<K> keys = new ArrayList<K>();
			while (res.next()) {
				keys.add((K) res.getObject(1));
			}
			holder.setKeys(keys);
		} catch (Exception e) {
			throw new DataAccessException("数据库访问异常", e);
		} finally {
			// Connection要在事务管理中关闭
			// dao层不能关Connection
			JDBCUtil.close(res, ps, null);
		}
	}

	/**
	 * 执行查询操作
	 * @param sql 要执行的预编译查询语句
	 * @param params 与预编译语句中占位符对应的参数
	 * @param mapper 对结果集中一行数据进行处理的回调接口
	 * @return 查询结果
	 */
	public List<T> query(String sql, Object[] params, RowMapper<T> mapper) {
		Connection con = null;
		PreparedStatement ps = null;
		ResultSet res = null;
		try {
			con = JDBCUtil.getConnection();
			ps = con.prepareStatement(sql);
			if (params != null && params.length > 0) {
				for (int i = 0; i < params.length; i++) {
					ps.setObject(i + 1, params[i]);
				}
			}
			res = ps.executeQuery();
			List<T> list = new ArrayList<T>();
			while (res.next()) {
				T obj = mapper.mapRow(res);
				list.add(obj);
			}
			return list;
		} catch (Exception e) {
			throw new DataAccessException("数据库访问异常", e);
		} finally {
			JDBCUtil.close(res, ps, null);
		}
	}

	/**
	 * 执行查询操作
	 * @param sql 要执行的预编译语句
	 * @param params 与预编译语句中占位符对应的参数
	 * @param extractor 对整个结果集进行处理的回调接口
	 * @return 查询结果
	 */
	public Object query(String sql, Object[] params, ResultSetExtractor extractor) {
		Connection con = null;
		PreparedStatement ps = null;
		ResultSet res = null;
		try {
			con = JDBCUtil.getConnection();
			ps = con.prepareStatement(sql);
			if (params != null && params.length > 0) {
				for (int i = 0; i < params.length; i++) {
					ps.setObject(i + 1, params[i]);
				}
			}
			res = ps.executeQuery();
			return extractor.extractData(res);
		} catch (Exception e) {
			throw new DataAccessException("数据库访问异常", e);
		} finally {
			JDBCUtil.close(res, ps, null);
		}
	}

	/**
	 * 执行一次数据库操作
	 * @param executer 回调接口, 将Connection对象交给模板的使用者, 由使用者决定如何操作数据库
	 * @return 操作结果
	 */
	public Object execute(ConnectionCallback executer) {
		Connection con = null;
		try {
			con = JDBCUtil.getConnection();
			return executer.execute(con);
		} catch (Exception e) {
			throw new DataAccessException("数据库访问异常", e);
		}
	}

}
